// META: title=test WebNN API linear operation
// META: global=window,dedicatedworker
// META: variant=?cpu
// META: variant=?gpu
// META: variant=?npu
// META: script=../resources/utils.js
// META: timeout=long

'use strict';

// https://www.w3.org/TR/webnn/#api-mlgraphbuilder-linear
// Calculate a linear function y = alpha * x + beta on the input tensor.
//
// dictionary MLLinearOptions {
//   double alpha = 1;
//   double beta = 0;
// };
//
// MLOperand linear(MLOperand input, optional MLLinearOptions options = {});


const getLinearPrecisionTolerance = (graphResources) => {
  const toleranceValueDict = {float32: 2, float16: 2};
  const expectedDataType =
      getExpectedDataTypeOfSingleOutput(graphResources.expectedOutputs);
  return {metricType: 'ULP', value: toleranceValueDict[expectedDataType]};
};

const linearTests = [
  {
    'name': 'linear float32 1D constant tensor default options',
    'graph': {
      'inputs': {
        'linearInput': {
          'data': [
            -1.12251615524292,   -6.605223178863525, -1.9555538892745972,
            -4.598548412322998,  4.234208106994629,  3.0975420475006104,
            3.7465922832489014,  -4.487029552459717, 6.407402038574219,
            -4.354544162750244,  -5.819092750549316, 3.7214345932006836,
            -6.330113887786865,  8.580595016479492,  -6.764922142028809,
            6.433565616607666,   -9.708685874938965, 2.6431379318237305,
            5.2140889167785645,  9.65861701965332,   -8.721749305725098,
            -0.4533396363258362, 9.992619514465332,  -6.469675064086914
          ],
          'descriptor': {'dimensions': [24], 'dataType': 'float32'},
          'constant': true
        }
      },
      'operators': [{
        'name': 'linear',
        'arguments': [{'input': 'linearInput'}],
        'outputs': 'linearOutput'
      }],
      'expectedOutputs': {
        'linearOutput': {
          'data': [
            -1.12251615524292,   -6.605223178863525, -1.9555538892745972,
            -4.598548412322998,  4.234208106994629,  3.0975420475006104,
            3.7465922832489014,  -4.487029552459717, 6.407402038574219,
            -4.354544162750244,  -5.819092750549316, 3.7214345932006836,
            -6.330113887786865,  8.580595016479492,  -6.764922142028809,
            6.433565616607666,   -9.708685874938965, 2.6431379318237305,
            5.2140889167785645,  9.65861701965332,   -8.721749305725098,
            -0.4533396363258362, 9.992619514465332,  -6.469675064086914
          ],
          'descriptor': {'dimensions': [24], 'dataType': 'float32'}
        }
      }
    }
  },
  {
    'name': 'linear float32 1D tensor default options',
    'graph': {
      'inputs': {
        'linearInput': {
          'data': [
            -1.12251615524292,   -6.605223178863525, -1.9555538892745972,
            -4.598548412322998,  4.234208106994629,  3.0975420475006104,
            3.7465922832489014,  -4.487029552459717, 6.407402038574219,
            -4.354544162750244,  -5.819092750549316, 3.7214345932006836,
            -6.330113887786865,  8.580595016479492,  -6.764922142028809,
            6.433565616607666,   -9.708685874938965, 2.6431379318237305,
            5.2140889167785645,  9.65861701965332,   -8.721749305725098,
            -0.4533396363258362, 9.992619514465332,  -6.469675064086914
          ],
          'descriptor': {'dimensions': [24], 'dataType': 'float32'}
        }
      },
      'operators': [{
        'name': 'linear',
        'arguments': [{'input': 'linearInput'}],
        'outputs': 'linearOutput'
      }],
      'expectedOutputs': {
        'linearOutput': {
          'data': [
            -1.12251615524292,   -6.605223178863525, -1.9555538892745972,
            -4.598548412322998,  4.234208106994629,  3.0975420475006104,
            3.7465922832489014,  -4.487029552459717, 6.407402038574219,
            -4.354544162750244,  -5.819092750549316, 3.7214345932006836,
            -6.330113887786865,  8.580595016479492,  -6.764922142028809,
            6.433565616607666,   -9.708685874938965, 2.6431379318237305,
            5.2140889167785645,  9.65861701965332,   -8.721749305725098,
            -0.4533396363258362, 9.992619514465332,  -6.469675064086914
          ],
          'descriptor': {'dimensions': [24], 'dataType': 'float32'}
        }
      }
    }
  },
  {
    'name': 'linear float32 2D tensor default options',
    'graph': {
      'inputs': {
        'linearInput': {
          'data': [
            -1.12251615524292,   -6.605223178863525, -1.9555538892745972,
            -4.598548412322998,  4.234208106994629,  3.0975420475006104,
            3.7465922832489014,  -4.487029552459717, 6.407402038574219,
            -4.354544162750244,  -5.819092750549316, 3.7214345932006836,
            -6.330113887786865,  8.580595016479492,  -6.764922142028809,
            6.433565616607666,   -9.708685874938965, 2.6431379318237305,
            5.2140889167785645,  9.65861701965332,   -8.721749305725098,
            -0.4533396363258362, 9.992619514465332,  -6.469675064086914
          ],
          'descriptor': {'dimensions': [4, 6], 'dataType': 'float32'}
        }
      },
      'operators': [{
        'name': 'linear',
        'arguments': [{'input': 'linearInput'}],
        'outputs': 'linearOutput'
      }],
      'expectedOutputs': {
        'linearOutput': {
          'data': [
            -1.12251615524292,   -6.605223178863525, -1.9555538892745972,
            -4.598548412322998,  4.234208106994629,  3.0975420475006104,
            3.7465922832489014,  -4.487029552459717, 6.407402038574219,
            -4.354544162750244,  -5.819092750549316, 3.7214345932006836,
            -6.330113887786865,  8.580595016479492,  -6.764922142028809,
            6.433565616607666,   -9.708685874938965, 2.6431379318237305,
            5.2140889167785645,  9.65861701965332,   -8.721749305725098,
            -0.4533396363258362, 9.992619514465332,  -6.469675064086914
          ],
          'descriptor': {'dimensions': [4, 6], 'dataType': 'float32'}
        }
      }
    }
  },
  {
    'name': 'linear float32 3D tensor default options',
    'graph': {
      'inputs': {
        'linearInput': {
          'data': [
            -1.12251615524292,   -6.605223178863525, -1.9555538892745972,
            -4.598548412322998,  4.234208106994629,  3.0975420475006104,
            3.7465922832489014,  -4.487029552459717, 6.407402038574219,
            -4.354544162750244,  -5.819092750549316, 3.7214345932006836,
            -6.330113887786865,  8.580595016479492,  -6.764922142028809,
            6.433565616607666,   -9.708685874938965, 2.6431379318237305,
            5.2140889167785645,  9.65861701965332,   -8.721749305725098,
            -0.4533396363258362, 9.992619514465332,  -6.469675064086914
          ],
          'descriptor': {'dimensions': [2, 3, 4], 'dataType': 'float32'}
        }
      },
      'operators': [{
        'name': 'linear',
        'arguments': [{'input': 'linearInput'}],
        'outputs': 'linearOutput'
      }],
      'expectedOutputs': {
        'linearOutput': {
          'data': [
            -1.12251615524292,   -6.605223178863525, -1.9555538892745972,
            -4.598548412322998,  4.234208106994629,  3.0975420475006104,
            3.7465922832489014,  -4.487029552459717, 6.407402038574219,
            -4.354544162750244,  -5.819092750549316, 3.7214345932006836,
            -6.330113887786865,  8.580595016479492,  -6.764922142028809,
            6.433565616607666,   -9.708685874938965, 2.6431379318237305,
            5.2140889167785645,  9.65861701965332,   -8.721749305725098,
            -0.4533396363258362, 9.992619514465332,  -6.469675064086914
          ],
          'descriptor': {'dimensions': [2, 3, 4], 'dataType': 'float32'}
        }
      }
    }
  },
  {
    'name': 'linear float32 4D tensor default options',
    'graph': {
      'inputs': {
        'linearInput': {
          'data': [
            -1.12251615524292,   -6.605223178863525, -1.9555538892745972,
            -4.598548412322998,  4.234208106994629,  3.0975420475006104,
            3.7465922832489014,  -4.487029552459717, 6.407402038574219,
            -4.354544162750244,  -5.819092750549316, 3.7214345932006836,
            -6.330113887786865,  8.580595016479492,  -6.764922142028809,
            6.433565616607666,   -9.708685874938965, 2.6431379318237305,
            5.2140889167785645,  9.65861701965332,   -8.721749305725098,
            -0.4533396363258362, 9.992619514465332,  -6.469675064086914
          ],
          'descriptor': {'dimensions': [2, 2, 2, 3], 'dataType': 'float32'}
        }
      },
      'operators': [{
        'name': 'linear',
        'arguments': [{'input': 'linearInput'}],
        'outputs': 'linearOutput'
      }],
      'expectedOutputs': {
        'linearOutput': {
          'data': [
            -1.12251615524292,   -6.605223178863525, -1.9555538892745972,
            -4.598548412322998,  4.234208106994629,  3.0975420475006104,
            3.7465922832489014,  -4.487029552459717, 6.407402038574219,
            -4.354544162750244,  -5.819092750549316, 3.7214345932006836,
            -6.330113887786865,  8.580595016479492,  -6.764922142028809,
            6.433565616607666,   -9.708685874938965, 2.6431379318237305,
            5.2140889167785645,  9.65861701965332,   -8.721749305725098,
            -0.4533396363258362, 9.992619514465332,  -6.469675064086914
          ],
          'descriptor': {'dimensions': [2, 2, 2, 3], 'dataType': 'float32'}
        }
      }
    }
  },
  {
    'name': 'linear float32 5D tensor default options',
    'graph': {
      'inputs': {
        'linearInput': {
          'data': [
            -1.12251615524292,   -6.605223178863525, -1.9555538892745972,
            -4.598548412322998,  4.234208106994629,  3.0975420475006104,
            3.7465922832489014,  -4.487029552459717, 6.407402038574219,
            -4.354544162750244,  -5.819092750549316, 3.7214345932006836,
            -6.330113887786865,  8.580595016479492,  -6.764922142028809,
            6.433565616607666,   -9.708685874938965, 2.6431379318237305,
            5.2140889167785645,  9.65861701965332,   -8.721749305725098,
            -0.4533396363258362, 9.992619514465332,  -6.469675064086914
          ],
          'descriptor': {'dimensions': [2, 1, 4, 1, 3], 'dataType': 'float32'}
        }
      },
      'operators': [{
        'name': 'linear',
        'arguments': [{'input': 'linearInput'}],
        'outputs': 'linearOutput'
      }],
      'expectedOutputs': {
        'linearOutput': {
          'data': [
            -1.12251615524292,   -6.605223178863525, -1.9555538892745972,
            -4.598548412322998,  4.234208106994629,  3.0975420475006104,
            3.7465922832489014,  -4.487029552459717, 6.407402038574219,
            -4.354544162750244,  -5.819092750549316, 3.7214345932006836,
            -6.330113887786865,  8.580595016479492,  -6.764922142028809,
            6.433565616607666,   -9.708685874938965, 2.6431379318237305,
            5.2140889167785645,  9.65861701965332,   -8.721749305725098,
            -0.4533396363258362, 9.992619514465332,  -6.469675064086914
          ],
          'descriptor': {'dimensions': [2, 1, 4, 1, 3], 'dataType': 'float32'}
        }
      }
    }
  },
  {
    'name':
        'linear float32 4D tensor specified options.alpha and default options.beta',
    'graph': {
      'inputs': {
        'linearInput': {
          'data': [
            -1.12251615524292,   -6.605223178863525, -1.9555538892745972,
            -4.598548412322998,  4.234208106994629,  3.0975420475006104,
            3.7465922832489014,  -4.487029552459717, 6.407402038574219,
            -4.354544162750244,  -5.819092750549316, 3.7214345932006836,
            -6.330113887786865,  8.580595016479492,  -6.764922142028809,
            6.433565616607666,   -9.708685874938965, 2.6431379318237305,
            5.2140889167785645,  9.65861701965332,   -8.721749305725098,
            -0.4533396363258362, 9.992619514465332,  -6.469675064086914
          ],
          'descriptor': {'dimensions': [2, 2, 2, 3], 'dataType': 'float32'}
        }
      },
      'operators': [{
        'name': 'linear',
        'arguments': [
          {'input': 'linearInput'}, {'options': {'alpha': 7.398793812746618}}
        ],
        'outputs': 'linearOutput'
      }],
      'expectedOutputs': {
        'linearOutput': {
          'data': [
            -8.305265426635742,  -48.87068176269531,  -14.46873950958252,
            -34.023712158203125, 31.328031539916992,  22.918073654174805,
            27.72026252746582,   -33.198604583740234, 47.407047271728516,
            -32.2183723449707,   -43.05426788330078,  27.53412628173828,
            -46.835205078125,    63.486053466796875,  -50.05226516723633,
            47.600624084472656,  -71.83256530761719,  19.556032180786133,
            38.57796859741211,   71.46211242675781,   -64.53042602539062,
            -3.3541665077209473, 73.9333267211914,    -47.86779022216797
          ],
          'descriptor': {'dimensions': [2, 2, 2, 3], 'dataType': 'float32'}
        }
      }
    }
  },
  {
    'name':
        'linear float32 positive 4D tensor specified positive options.beta and default options.alpha',
    'graph': {
      'inputs': {
        'linearInput': {
          'data': [
            5.098546028137207,  3.381463050842285,   8.054762840270996,
            8.074773788452148,  0.47079092264175415, 5.243824005126953,
            3.827306032180786,  5.3697686195373535,  6.1033172607421875,
            3.7505786418914795, 0.7479738593101501,  1.8931976556777954,
            1.9056464433670044, 7.863316059112549,   4.58075475692749,
            9.373635292053223,  6.584214210510254,   9.344809532165527,
            5.16057825088501,   0.8060914278030396,  9.130533218383789,
            3.1937403678894043, 5.748293399810791,   4.113487720489502
          ],
          'descriptor': {'dimensions': [2, 2, 2, 3], 'dataType': 'float32'}
        }
      },
      'operators': [{
        'name': 'linear',
        'arguments': [
          {'input': 'linearInput'}, {'options': {'beta': 5.919095653700928}}
        ],
        'outputs': 'linearOutput'
      }],
      'expectedOutputs': {
        'linearOutput': {
          'data': [
            11.017641067504883, 9.300558090209961,  13.973857879638672,
            13.99386978149414,  6.389886379241943,  11.162919998168945,
            9.7464017868042,    11.288864135742188, 12.02241325378418,
            9.669673919677734,  6.667069435119629,  7.81229305267334,
            7.824741840362549,  13.782411575317383, 10.499850273132324,
            15.292730331420898, 12.50330924987793,  15.263904571533203,
            11.079673767089844, 6.725186824798584,  15.049629211425781,
            9.112835884094238,  11.667388916015625, 10.032583236694336
          ],
          'descriptor': {'dimensions': [2, 2, 2, 3], 'dataType': 'float32'}
        }
      }
    }
  },
  {
    'name':
        'linear float32 negative 4D tensor specified negative options.beta and default options.alpha',
    'graph': {
      'inputs': {
        'linearInput': {
          'data': [
            -5.098546028137207,  -3.381463050842285,   -8.054762840270996,
            -8.074773788452148,  -0.47079092264175415, -5.243824005126953,
            -3.827306032180786,  -5.3697686195373535,  -6.1033172607421875,
            -3.7505786418914795, -0.7479738593101501,  -1.8931976556777954,
            -1.9056464433670044, -7.863316059112549,   -4.58075475692749,
            -9.373635292053223,  -6.584214210510254,   -9.344809532165527,
            -5.16057825088501,   -0.8060914278030396,  -9.130533218383789,
            -3.1937403678894043, -5.748293399810791,   -4.113487720489502
          ],
          'descriptor': {'dimensions': [2, 2, 2, 3], 'dataType': 'float32'}
        }
      },
      'operators': [{
        'name': 'linear',
        'arguments': [
          {'input': 'linearInput'}, {'options': {'beta': -5.919095653700928}}
        ],
        'outputs': 'linearOutput'
      }],
      'expectedOutputs': {
        'linearOutput': {
          'data': [
            -11.017641067504883, -9.300558090209961,  -13.973857879638672,
            -13.99386978149414,  -6.389886379241943,  -11.162919998168945,
            -9.7464017868042,    -11.288864135742188, -12.02241325378418,
            -9.669673919677734,  -6.667069435119629,  -7.81229305267334,
            -7.824741840362549,  -13.782411575317383, -10.499850273132324,
            -15.292730331420898, -12.50330924987793,  -15.263904571533203,
            -11.079673767089844, -6.725186824798584,  -15.049629211425781,
            -9.112835884094238,  -11.667388916015625, -10.032583236694336
          ],
          'descriptor': {'dimensions': [2, 2, 2, 3], 'dataType': 'float32'}
        }
      }
    }
  },
  {
    'name':
        'linear float32 positive 4D tensor all options (positive options.alpha and positive options.beta)',
    'graph': {
      'inputs': {
        'linearInput': {
          'data': [
            5.098546028137207,  3.381463050842285,   8.054762840270996,
            8.074773788452148,  0.47079092264175415, 5.243824005126953,
            3.827306032180786,  5.3697686195373535,  6.1033172607421875,
            3.7505786418914795, 0.7479738593101501,  1.8931976556777954,
            1.9056464433670044, 7.863316059112549,   4.58075475692749,
            9.373635292053223,  6.584214210510254,   9.344809532165527,
            5.16057825088501,   0.8060914278030396,  9.130533218383789,
            3.1937403678894043, 5.748293399810791,   4.113487720489502
          ],
          'descriptor': {'dimensions': [2, 2, 2, 3], 'dataType': 'float32'}
        }
      },
      'operators': [{
        'name': 'linear',
        'arguments': [
          {'input': 'linearInput'},
          {'options': {'alpha': 7.398793812746618, 'beta': 5.919095653700928}}
        ],
        'outputs': 'linearOutput'
      }],
      'expectedOutputs': {
        'linearOutput': {
          'data': [
            43.64218521118164,  30.937843322753906, 65.5146255493164,
            65.66268157958984,  9.402379989624023,  44.71706771850586,
            34.236541748046875, 45.64890670776367,  51.0762825012207,
            33.668853759765625, 11.45319938659668,  19.92647361755371,
            20.018579483032227, 64.09815216064453,  39.811153411865234,
            75.27268981933594,  54.63433837890625,  75.05941009521484,
            44.10115051269531,  11.883199691772461, 73.47402954101562,
            29.548921585083008, 48.44953155517578,  36.35394287109375
          ],
          'descriptor': {'dimensions': [2, 2, 2, 3], 'dataType': 'float32'}
        }
      }
    }
  },
  {
    'name':
        'linear float32 positive 4D tensor all options (negative options.alpha and negative options.beta)',
    'graph': {
      'inputs': {
        'linearInput': {
          'data': [
            5.098546028137207,  3.381463050842285,   8.054762840270996,
            8.074773788452148,  0.47079092264175415, 5.243824005126953,
            3.827306032180786,  5.3697686195373535,  6.1033172607421875,
            3.7505786418914795, 0.7479738593101501,  1.8931976556777954,
            1.9056464433670044, 7.863316059112549,   4.58075475692749,
            9.373635292053223,  6.584214210510254,   9.344809532165527,
            5.16057825088501,   0.8060914278030396,  9.130533218383789,
            3.1937403678894043, 5.748293399810791,   4.113487720489502
          ],
          'descriptor': {'dimensions': [2, 2, 2, 3], 'dataType': 'float32'}
        }
      },
      'operators': [{
        'name': 'linear',
        'arguments': [
          {'input': 'linearInput'},
          {'options': {'alpha': -7.398793812746618, 'beta': -5.919095653700928}}
        ],
        'outputs': 'linearOutput'
      }],
      'expectedOutputs': {
        'linearOutput': {
          'data': [
            -43.64218521118164,  -30.937843322753906, -65.5146255493164,
            -65.66268157958984,  -9.402379989624023,  -44.71706771850586,
            -34.236541748046875, -45.64890670776367,  -51.0762825012207,
            -33.668853759765625, -11.45319938659668,  -19.92647361755371,
            -20.018579483032227, -64.09815216064453,  -39.811153411865234,
            -75.27268981933594,  -54.63433837890625,  -75.05941009521484,
            -44.10115051269531,  -11.883199691772461, -73.47402954101562,
            -29.548921585083008, -48.44953155517578,  -36.35394287109375
          ],
          'descriptor': {'dimensions': [2, 2, 2, 3], 'dataType': 'float32'}
        }
      }
    }
  },
  {
    'name':
        'linear float32 negative 4D tensor all options (positive options.alpha and negative options.beta)',
    'graph': {
      'inputs': {
        'linearInput': {
          'data': [
            -5.098546028137207,  -3.381463050842285,   -8.054762840270996,
            -8.074773788452148,  -0.47079092264175415, -5.243824005126953,
            -3.827306032180786,  -5.3697686195373535,  -6.1033172607421875,
            -3.7505786418914795, -0.7479738593101501,  -1.8931976556777954,
            -1.9056464433670044, -7.863316059112549,   -4.58075475692749,
            -9.373635292053223,  -6.584214210510254,   -9.344809532165527,
            -5.16057825088501,   -0.8060914278030396,  -9.130533218383789,
            -3.1937403678894043, -5.748293399810791,   -4.113487720489502
          ],
          'descriptor': {'dimensions': [2, 2, 2, 3], 'dataType': 'float32'}
        }
      },
      'operators': [{
        'name': 'linear',
        'arguments': [
          {'input': 'linearInput'},
          {'options': {'alpha': 7.398793812746618, 'beta': -5.919095653700928}}
        ],
        'outputs': 'linearOutput'
      }],
      'expectedOutputs': {
        'linearOutput': {
          'data': [
            -43.64218521118164,  -30.937843322753906, -65.5146255493164,
            -65.66268157958984,  -9.402379989624023,  -44.71706771850586,
            -34.236541748046875, -45.64890670776367,  -51.0762825012207,
            -33.668853759765625, -11.45319938659668,  -19.92647361755371,
            -20.018579483032227, -64.09815216064453,  -39.811153411865234,
            -75.27268981933594,  -54.63433837890625,  -75.05941009521484,
            -44.10115051269531,  -11.883199691772461, -73.47402954101562,
            -29.548921585083008, -48.44953155517578,  -36.35394287109375
          ],
          'descriptor': {'dimensions': [2, 2, 2, 3], 'dataType': 'float32'}
        }
      }
    }
  }
];

if (navigator.ml) {
  linearTests.forEach((test) => {
    webnn_conformance_test(
        buildGraphAndCompute, getLinearPrecisionTolerance, test);
  });
} else {
  test(() => assert_implements(navigator.ml, 'missing navigator.ml'));
}
